// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package ssa

// 临界分割临界边（从具有
// 多个外边缘的块到具有多个内边缘的块）。
// Regalloc想要一个无临界边缘的CFG，这样它就可以实现phi值。
func critical(f *Func) {
	// 从phi arg ID映射到为该参数创建的新块
	blocks := make([]*Block, f.NumValues())
	// 需要在f上迭代。没有范围的块，因为我们可能
	// 需要在新构造的块上分割临界边
	for j := 0; j < len(f.Blocks); j++ {
		b := f.Blocks[j]
		if len(b.Preds) <= 1 {
			continue
		}

		var phi *Value
		// 确定在这个
		// 块中是否只有一个phi，这比具有多个phi值的块的一般
		// 情况更容易处理。
		for _, v := range b.Values {
			if v.Op == OpPhi {
				if phi != nil {
					phi = nil
					break
				}
				phi = v
			}
		}

		// 重置我们的块映射
		if phi != nil {
			for _, v := range phi.Args {
				blocks[v.ID] = nil
			}
		}

		// 分割来自多个输出块的输入边。
		for i := 0; i < len(b.Preds); {
			e := b.Preds[i]
			p := e.b
			pi := e.i
			if p.Kind == BlockPlain {
				i++
				continue // 仅单个输出块
			}

			var d *Block         // 用于删除临界边的新块
			reusedBlock := false // 如果为真，则这不是第一次使用此块
			if phi != nil {
				argID := phi.Args[i].ID
				// 查找或记录我们用于拆分
				// 此参数的临界边
				if d = blocks[argID]; d == nil {
					// 拆分不一定删除临界边，
					// 由于我们在上面的len（f.Blocks）上进行迭代，这迫使
					// 重新检查新的块。
					d = f.NewBlock(BlockPlain)
					d.Pos = p.Pos
					blocks[argID] = d
					if f.pass.debug > 0 {
						f.Warnl(p.Pos, "split critical edge")
					}
				} else {
					reusedBlock = true
				}
			} else {
				// 没有现有块，因此分配一个新块
				// 放置在边缘
				d = f.NewBlock(BlockPlain)
				d.Pos = p.Pos
				if f.pass.debug > 0 {
					f.Warnl(p.Pos, "split critical edge")
				}
			}

			// 如果这不是
			// 块的第一个参数，然后我们需要从块中删除相应的元素
			// 
			// 前置和phi参数
			if reusedBlock {
				// 添加p->d边
				p.Succs[pi] = Edge{d, len(d.Preds)}
				d.Preds = append(d.Preds, Edge{p, pi})

				// 从b中删除p作为前置。
				b.removePred(i)

				// 更新相应的phi参数
				b.removePhiArg(phi, i)

				// 拆分偶尔会导致phi具有
				// 单个参数（与-N一起出现）
				// TODO（cuonglm，khr）：将其替换为phielimValue，
				// make removePhiArg将其合并。
				if len(b.Preds) == 1 {
					phi.Op = OpCopy
				}
				// 在这种情况下不要增加i，因为我们将
				// 一个未经处理的前置机移到插槽i中。
			} else {
				// 将其拼接到
				p.Succs[pi] = Edge{d, 0}
				b.Preds[i] = Edge{d, 0}
				d.Preds = append(d.Preds, Edge{p, pi})
				d.Succs = append(d.Succs, Edge{b, i})
				i++
			}
		}
	}
}
